<template>
  <div
    :style="style"
    :class="[
      {
        [classNameActive]: enabled,
        [classNameDragging]: dragging,
        [classNameResizing]: resizing,
        [classNameDraggable]: draggable,
        [classNameResizable]: resizable,
        [classNameRotating]: rotating,
        [classNameRotatable]: rotatable,
      },
      className,
    ]"
    @mousedown="elementMouseDown"
    @touchstart="elementTouchDown"
  >
    <div
      v-for="(handle, index) in actualHandles"
      :key="index"
      :class="[classNameHandle, classNameHandle + '-' + handle]"
      :style="handleStyle(handle, index)"
      @mousedown.stop.prevent="handleDown(handle, $event)"
      @touchstart.stop.prevent="handleTouchDown(handle, $event)"
    >
      <slot :name="handle"></slot>
    </div>
    <slot></slot>
  </div>
</template>

<script>
import {  $emit } from "./utils/gogocodeTransfer";
import { matchesSelectorToParentElements, getComputedSize, addEvent, removeEvent } from "./utils/dom";
import { computeWidth, computeHeight, restrictToBounds, snapToGrid, rotatedPoint, getAngle } from "./utils/fns";

export const events = {
  mouse: {
    start: "mousedown",
    move: "mousemove",
    stop: "mouseup",
  },
  touch: {
    start: "touchstart",
    move: "touchmove",
    stop: "touchend",
  },
};
// 禁止用户选取
export const userSelectNone = {
  userSelect: "none",
  MozUserSelect: "none",
  WebkitUserSelect: "none",
  MsUserSelect: "none",
};
// 用户选中自动
export const userSelectAuto = {
  userSelect: "auto",
  MozUserSelect: "auto",
  WebkitUserSelect: "auto",
  MsUserSelect: "auto",
};

let eventsFor = events.mouse;

export default {
  name: "VueDragResizeRotate",
  props: {
    className: {
      type: String,
      default: "vue-drag-resize-rotate",
    },
    classNameDraggable: {
      type: String,
      default: "draggable",
    },
    classNameResizable: {
      type: String,
      default: "resizable",
    },
    // 新增开启旋转时的自定义类名
    classNameRotatable: {
      type: String,
      default: "rotatable",
    },
    classNameDragging: {
      type: String,
      default: "dragging",
    },
    classNameResizing: {
      type: String,
      default: "resizing",
    },
    // 新增组件处于旋转时的自定义类名
    classNameRotating: {
      type: String,
      default: "rotating",
    },
    classNameActive: {
      type: String,
      default: "active",
    },
    classNameHandle: {
      type: String,
      default: "handle",
    },
    disableUserSelect: {
      type: Boolean,
      default: true,
    },
    enableNativeDrag: {
      type: Boolean,
      default: false,
    },
    preventDeactivation: {
      type: Boolean,
      default: false,
    },
    active: {
      type: Boolean,
      default: false,
    },
    draggable: {
      type: Boolean,
      default: true,
    },
    resizable: {
      type: Boolean,
      default: true,
    },
    // 新增 旋转 默认为false 不开启
    rotatable: {
      type: Boolean,
      default: false,
    },
    // 锁定宽高比
    lockAspectRatio: {
      type: Boolean,
      default: false,
    },
    // 新增 外部传入纵横比 w/h
    outsideAspectRatio: {
      type: [Number, String],
      default: 0,
    },
    w: {
      type: [Number, String],
      default: 200,
      validator: (val) => {
        if (typeof val === "number") {
          return val > 0;
        }
        return val === "auto";
      },
    },
    h: {
      type: [Number, String],
      default: 200,
      validator: (val) => {
        if (typeof val === "number") {
          return val > 0;
        }
        return val === "auto";
      },
    },
    minWidth: {
      type: Number,
      default: 0,
      validator: (val) => val >= 0,
    },
    minHeight: {
      type: Number,
      default: 0,
      validator: (val) => val >= 0,
    },
    maxWidth: {
      type: Number,
      default: Infinity,
      validator: (val) => val >= 0,
    },
    maxHeight: {
      type: Number,
      default: Infinity,
      validator: (val) => val >= 0,
    },
    x: {
      type: [String, Number],
      default: 0,
    },
    y: {
      type: [String, Number],
      default: 0,
    },
    z: {
      type: [String, Number],
      default: "auto",
      validator: (val) => (typeof val === "string" ? val === "auto" : val >= 0),
    },
    // 新增 初始旋转角度
    r: {
      type: [String, Number],
      default: 0,
    },
    // 新增 旋转手柄 rot
    handles: {
      type: Array,
      default: () => ["tl", "tm", "tr", "mr", "br", "bm", "bl", "ml", "rot"],
      validator: (val) => {
        const s = new Set(["tl", "tm", "tr", "mr", "br", "bm", "bl", "ml", "rot"]);
        return new Set(val.filter((h) => s.has(h))).size === val.length;
      },
    },
    dragHandle: {
      type: String,
      default: null,
    },
    dragCancel: {
      type: String,
      default: null,
    },
    axis: {
      type: String,
      default: "both",
      validator: (val) => ["x", "y", "both"].includes(val),
    },
    grid: {
      type: Array,
      default: () => [1, 1],
    },
    parent: {
      type: [Boolean, String],
      default: false,
    },
    onDragStart: {
      type: Function,
      default: () => true,
    },
    onDrag: {
      type: Function,
      default: () => true,
    },
    onResizeStart: {
      type: Function,
      default: () => true,
    },
    onResize: {
      type: Function,
      default: () => true,
    },
    // 新增 回调事件
    onRotateStart: {
      type: Function,
      default: () => true,
    },
    onRotate: {
      type: Function,
      default: () => true,
    },
    // 冲突检测
    isConflictCheck: {
      type: Boolean,
      default: false,
    },
    // 元素对齐
    snap: {
      type: Boolean,
      default: false,
    },
    // 新增 是否对齐容器边界
    snapBorder: {
      type: Boolean,
      default: false,
    },
    // 当调用对齐时，用来设置组件与组件之间的对齐距离，以像素为单位
    snapTolerance: {
      type: Number,
      default: 5,
      validator: function (val) {
        return typeof val === "number";
      },
    },
    // 缩放比例
    scaleRatio: {
      type: Number,
      default: 1,
      validator: (val) => typeof val === "number",
    },
    // handle是否缩放
    handleInfo: {
      type: Object,
      default: () => {
        return {
          size: 8,
          offset: -4,
          switch: true,
        };
      },
    },
  },
  data: function () {
    return {
      left: this.x,
      top: this.y,
      right: null,
      bottom: null,
      // 新增旋转角度
      rotate: this.r,
      width: null,
      height: null,
      widthTouched: false,
      heightTouched: false,
      // 纵横比变量
      aspectFactor: null,
      // 容器的大小
      parentWidth: null,
      parentHeight: null,
      // 设置最小和最大尺寸
      minW: this.minWidth,
      minH: this.minHeight,
      maxW: this.maxWidth,
      maxH: this.maxHeight,
      // 定义控制手柄
      handle: null,
      enabled: this.active,
      resizing: false,
      dragging: false,
      // 新增 表明组件是否正处于旋转状态
      rotating: false,
      zIndex: this.z,
      // 父元素左上角的坐标值
      parentX: 0,
      parentY: 0,
    };
  },
  computed: {
    handleStyle() {
      return (stick) => {
        if (!this.handleInfo.switch) return { display: this.enabled ? "block" : "none" };
        // 新增 当没有开启旋转的时候，旋转手柄不显示
        if (stick === "rot" && !this.rotatable) return { display: "none" };
        if (stick !== "rot" && !this.resizable) return { display: "none" };
        const size = (this.handleInfo.size / this.scaleRatio).toFixed(2);
        const offset = (this.handleInfo.offset / this.scaleRatio).toFixed(2);
        const center = (size / 2).toFixed(2);
        const styleMap = {
          tl: {
            top: `${offset}px`,
            left: `${offset}px`,
          },
          tm: {
            top: `${offset}px`,
            left: `calc(50% - ${center}px)`,
          },
          tr: {
            top: `${offset}px`,
            right: `${offset}px`,
          },
          mr: {
            top: `calc(50% - ${center}px)`,
            right: `${offset}px`,
          },
          br: {
            bottom: `${offset}px`,
            right: `${offset}px`,
          },
          bm: {
            bottom: `${offset}px`,
            right: `calc(50% - ${center}px)`,
          },
          bl: {
            bottom: `${offset}px`,
            left: `${offset}px`,
          },
          ml: {
            top: `calc(50% - ${center}px)`,
            left: `${offset}px`,
          },
          rot: {
            top: `-${size * 3}px`,
            left: `50%`,
          },
        };
        const stickStyle = {
          width: styleMap[stick].width || `${size}px`,
          height: styleMap[stick].height || `${size}px`,
          top: styleMap[stick].top,
          left: styleMap[stick].left,
          right: styleMap[stick].right,
          bottom: styleMap[stick].bottom,
        };
        const mapStick2Index = {
          tl: 0,
          tm: 1,
          tr: 2,
          mr: 3,
          br: 4,
          bm: 5,
          bl: 6,
          ml: 7,
          rot: 8,
        };
        // 新增 让控制手柄的鼠标样式跟随旋转角度变化
        if (stick !== "rot") {
          const cursorStyleArray = [
            "nw-resize",
            "n-resize",
            "ne-resize",
            "e-resize",
            "se-resize",
            "s-resize",
            "sw-resize",
            "w-resize",
          ];
          const STEP = 45;
          const rotate = this.rotate + STEP / 2;
          const deltaIndex = Math.floor(rotate / STEP);
          let index = (mapStick2Index[stick] + deltaIndex) % 8;
          stickStyle.cursor = cursorStyleArray[index];
        }
        stickStyle.display = this.enabled ? "block" : "none";
        return stickStyle;
      };
    },
    style() {
      return {
        transform: `translate(${this.left}px, ${this.top}px) rotate(${this.rotate}deg)`,
        width: this.computedWidth,
        height: this.computedHeight,
        zIndex: this.zIndex,
        fontSize: this.handleInfo.size * 2 + "px",
        ...(this.dragging && this.disableUserSelect ? userSelectNone : userSelectAuto),
      };
    },
    // 控制柄显示与否
    actualHandles() {
      if (!this.resizable && !this.rotatable) return [];
      return this.handles;
    },
    //  根据left right 算出元素的宽度
    computedWidth() {
      if (this.w === "auto") {
        if (!this.widthTouched) {
          return "auto";
        }
      }
      return this.width + "px";
    },
    // 根据top bottom 算出元素的宽度
    computedHeight() {
      if (this.h === "auto") {
        if (!this.heightTouched) {
          return "auto";
        }
      }
      return this.height + "px";
    },
  },
  watch: {
    active(val) {
      this.enabled = val;
      if (val) {
        this.updateParentSize();
        $emit(this, "activated");
      } else {
        $emit(this, "deactivated");
      }
    },
    x(val) {
      if (this.resizing || this.dragging) {
        return;
      }
      if (this.parent) {
        this.bounds = this.calcDragLimits();
      }
      this.moveHorizontally(val);
    },
    y(val) {
      if (this.resizing || this.dragging) {
        return;
      }
      if (this.parent) {
        this.bounds = this.calcDragLimits();
      }
      this.moveVertically(val);
    },
    z(val) {
      if (val >= 0 || val === "auto") {
        this.zIndex = val;
      }
    },
    // 新增 监听外部传入参数  旋转角度
    r(val) {
      if (val >= 0) {
        this.rotate = val % 360;
      }
    },
    // 锁定纵横比
    lockAspectRatio(val) {
      if (val) {
        if (this.outsideAspectRatio) {
          this.aspectFactor = this.outsideAspectRatio;
        } else {
          this.aspectFactor = this.width / this.height;
        }
      } else {
        this.aspectFactor = undefined;
      }
    },
    // 自定义纵横比
    outsideAspectRatio(val) {
      if (val) {
        this.aspectFactor = val;
      }
    },
    minWidth(val) {
      if (val > 0) {
        this.minW = val;
      }
      this.correctSize();
    },
    minHeight(val) {
      if (val > 0) {
        this.minH = val;
      }
      this.correctSize();
    },
    maxWidth(val) {
      if(val > 0) {
        this.maxW = val;
      }
      this.correctSize()
    },
    maxHeight(val) {
      if (val > 0) {
        this.maxH = val;
      }
      this.correctSize()
    },
    w(val) {
      if (this.resizing || this.dragging) {
        return;
      }
      if (this.parent) {
        this.bounds = this.calcResizeLimits();
      }
      this.changeWidth(val);
    },
    h(val) {
      if (this.resizing || this.dragging) {
        return;
      }
      if (this.parent) {
        this.bounds = this.calcResizeLimits();
      }
      this.changeHeight(val);
    },
  },
  created() {
    if (this.maxWidth && this.minWidth > this.maxWidth) {
      console.warn("[Vdr warn]: Invalid prop: minWidth cannot be greater than maxWidth");
    }

    if (this.maxHeight && this.minHeight > this.maxHeight) {
      console.warn("[Vdr warn]: Invalid prop: minHeight cannot be greater than maxHeight");
    }

    this.Center = {x: 0, y: 0}
    this.TL = { x: 0, y: 0};
    this.TR = { x: 0, y: 0};
    this.BL = { x: 0, y: 0};
    this.BR = { x: 0, y: 0};

    this.resetBoundsAndMouseState();
  },
  mounted() {
    if (!this.enableNativeDrag) {
      this.$el.ondragstart = () => false;
    }
    const [parentWidth, parentHeight] = this.getParentSize();
    this.parentWidth = parentWidth;
    this.parentHeight = parentHeight;
    const [width, height] = getComputedSize(this.$el);
    this.aspectFactor = (this.w !== "auto" ? this.w : width) / (this.h !== "auto" ? this.h : height);
    if (this.outsideAspectRatio) {
      this.aspectFactor = this.outsideAspectRatio;
    }
    this.width = this.w !== "auto" ? this.w : width;
    this.height = this.h !== "auto" ? this.h : height;
    this.right = this.parentWidth - this.width - this.left;
    this.bottom = this.parentHeight - this.height - this.top;

    // 绑定data-*属性
    this.settingAttribute();
    // 监听取消操作
    addEvent(document.documentElement, "mousedown", this.deselect);
    addEvent(document.documentElement, "touchend touchcancel", this.deselect);
    //  窗口变化时，检查容器大小
    this.checkParentSize()
    addEvent(window, "resize", this.checkParentSize);
  },
  beforeUnmount: function () {
    removeEvent(document.documentElement, "mousedown", this.deselect);
    removeEvent(document.documentElement, "touchstart", this.handleUp);
    removeEvent(document.documentElement, "mousemove", this.move);
    removeEvent(document.documentElement, "touchmove", this.move);
    removeEvent(document.documentElement, "mouseup", this.handleUp);
    removeEvent(document.documentElement, "touchend touchcancel", this.deselect);
    removeEvent(window, "resize", this.checkParentSize);
  },
  methods: {
    // 修正宽高
    correctSize() {
      if (this.width < this.minW) {
        this.width = this.minW;
      }
      if (this.height < this.minH) {
        this.height = this.minH;
      }
      if (this.height > this.maxH) {
        this.height = this.maxH;        
      }
      if (this.width > this.maxW) {
        this.width = this.maxW;
      }
      $emit(this, "resizestop", this.left, this.top, this.width, this.height);
    },
    // 重置边界和鼠标状态
    resetBoundsAndMouseState() {
      this.mouseClickPosition = {
        mouseX: 0,
        mouseY: 0,
        x: 0,
        y: 0,
        w: 0,
        h: 0,
      };
      this.bounds = {
        minLeft: null,
        maxLeft: null,
        minRight: null,
        maxRight: null,
        minTop: null,
        maxTop: null,
        minBottom: null,
        maxBottom: null,
      };
    },
    // 检查父元素大小
    checkParentSize() {
      if (this.parent) {
        const [newParentWidth, newParentHeight] = this.getParentSize();
        // 修复父元素改变大小后，组件resizing时活动异常
        this.right = newParentWidth - this.width - this.left;
        this.bottom = newParentHeight - this.height - this.top;
        this.parentWidth = newParentWidth;
        this.parentHeight = newParentHeight;
      }
    },
    // 更新获取父元素宽高
    updateParentSize() {
      const [parentWidth, parentHeight] = this.getParentSize();
      this.parentWidth = parentWidth;
      this.parentHeight = parentHeight;
    },
    // 获取父元素大小
    getParentSize() {
      if (this.parent === true) {
        const style = window.getComputedStyle(this.$el.parentNode, null);
        const rect = this.$el.parentNode.getBoundingClientRect();
        this.parentX = rect.x;
        this.parentY = rect.y;
        return [
          Math.round(parseFloat(style.getPropertyValue("width"), 10)),
          Math.round(parseFloat(style.getPropertyValue("height"), 10)),
        ];
      }
      if (typeof this.parent === "string") {
        const parentNode = document.querySelector(this.parent);
        if (!(parentNode instanceof HTMLElement)) {
          throw new Error(`The selector ${this.parent} does not match any element`);
        }
        return [parentNode.offsetWidth, parentNode.offsetHeight];
      }
      return [null, null];
    },
    // 元素触摸按下
    elementTouchDown(e) {
      eventsFor = events.touch;
      this.elementDown(e);
    },
    // 鼠标左键按下
    elementMouseDown(e) {
      eventsFor = events.mouse;
      this.elementDown(e);
    },
    // 元素按下
    elementDown(e) {
      if (e instanceof MouseEvent && e.which !== 1) {
        return;
      }
      const target = e.target || e.srcElement;
      if (this.$el.contains(target)) {
        if (this.onDragStart(e) === false) {
          return;
        }
        
        if (!this.enabled) {
          this.enabled = true;
          $emit(this, "activated");
          $emit(this, "update:active", true);
        }
        if (
          (this.dragHandle && !matchesSelectorToParentElements(target, this.dragHandle, this.$el)) ||
          (this.dragCancel && matchesSelectorToParentElements(target, this.dragCancel, this.$el))
        ) {
          this.dragging = false;
          return;
        }

        if (this.draggable) {
          this.dragging = true;
        }
        //  按下鼠标表示保存当前状态
        this.mouseClickPosition.mouseX = e.touches ? e.touches[0].pageX : e.pageX;
        this.mouseClickPosition.mouseY = e.touches ? e.touches[0].pageY : e.pageY;
        this.mouseClickPosition.left = this.left;
        this.mouseClickPosition.right = this.right;
        this.mouseClickPosition.top = this.top;
        this.mouseClickPosition.bottom = this.bottom;
        this.mouseClickPosition.width = this.width;
        this.mouseClickPosition.height = this.height;
        if (this.parent) {
          this.bounds = this.calcDragLimits();
        }
        addEvent(document.documentElement, eventsFor.move, this.move);
        addEvent(document.documentElement, eventsFor.stop, this.handleUp);
      }
    },
    // 计算移动范围
    calcDragLimits() {
      this.checkParentSize();
      // 开启旋转时，不在进行边界限制
      if (this.rotatable) {
        return {
          minLeft: -this.width / 2,
          maxLeft: this.parentWidth - this.width / 2,
          minRight: this.width / 2,
          maxRight: this.parentWidth + this.width / 2,
          minTop: -this.height / 2,
          maxTop: this.parentHeight - this.height / 2,
          minBottom: this.height / 2,
          maxBottom: this.parentHeight + this.height / 2,
        };
      } else {
        return {
          minLeft: this.left % this.grid[0],
          maxLeft: Math.floor((this.parentWidth - this.width - this.left) / this.grid[0]) * this.grid[0] + this.left,
          minRight: this.right % this.grid[0],
          maxRight: Math.floor((this.parentWidth - this.width - this.right) / this.grid[0]) * this.grid[0] + this.right,
          minTop: this.top % this.grid[1],
          maxTop: Math.floor((this.parentHeight - this.height - this.top) / this.grid[1]) * this.grid[1] + this.top,
          minBottom: this.bottom % this.grid[1],
          maxBottom:
            Math.floor((this.parentHeight - this.height - this.bottom) / this.grid[1]) * this.grid[1] + this.bottom,
        };
      }
    },
    // 取消选择
    deselect(e) {
      const target = e.target || e.srcElement;
      const regex = new RegExp(this.className + "-([trmbl]{2})", "");
      if (!this.$el.contains(target) && !regex.test(target.className)) {
        if (this.enabled && !this.preventDeactivation) {
          this.enabled = false;
          $emit(this, "deactivated");
          $emit(this, "update:active", false);
        }
        removeEvent(document.documentElement, eventsFor.move, this.move);
      }
      this.resetBoundsAndMouseState();
    },
    // 控制柄触摸按下
    handleTouchDown(handle, e) {
      eventsFor = events.touch;
      this.handleDown(handle, e);
    },
    // 控制柄按下
    handleDown(handle, e) {
      if (e instanceof MouseEvent && e.which !== 1) {
        return false;
      }
      if (this.onResizeStart(handle, e) === false) {
        return false;
      }
      if (e.stopPropagation) e.stopPropagation();
      this.handle = handle;
      // 新增旋转手柄
      if (this.handle === "rot") {
        this.rotating = true;
      } else {
        this.resizing = true;
      }
      // 新增保存矩形信息
      // 获取父元素的位置大小信息
      const rect = this.$el.getBoundingClientRect(); // 包围盒
      const { top, left, width, height } = rect;
      // 保存旋转中心的绝对坐标
      this.Center.x = window.pageXOffset + left + width / 2;
      this.Center.y = window.pageYOffset + top + height / 2;
      // 保存四个顶点的坐标
      let oleft = this.left;
      let otop = this.top;
      let owidth = this.width;
      let oheight = this.height;
      let centerX = oleft + owidth / 2;
      let centerY = otop + oheight / 2;
      let rotate = this.rotate;
      // 获取旋转后的坐标
      this.TL = rotatedPoint(centerX, centerY, oleft, otop, rotate);
      this.TR = rotatedPoint(centerX, centerY, oleft + owidth, otop, rotate);
      this.BL = rotatedPoint(centerX, centerY, oleft, otop + oheight, rotate);
      this.BR = rotatedPoint(centerX, centerY, oleft + owidth, otop + oheight, rotate);
      //  保存鼠标按下时的当前状态
      this.mouseClickPosition.mouseX = e.touches ? e.touches[0].pageX : e.pageX;
      this.mouseClickPosition.mouseY = e.touches ? e.touches[0].pageY : e.pageY;
      this.mouseClickPosition.left = this.left;
      this.mouseClickPosition.right = this.right;
      this.mouseClickPosition.top = this.top;
      this.mouseClickPosition.bottom = this.bottom;
      this.mouseClickPosition.width = this.width;
      this.mouseClickPosition.height = this.height;
      // 计算边界
      this.bounds = this.calcResizeLimits();
      // 添加事件
      addEvent(document.documentElement, eventsFor.move, this.move);
      addEvent(document.documentElement, eventsFor.stop, this.handleUp);
    },
    // 计算调整大小范围
    calcResizeLimits() {
      let minW = this.minW;
      let minH = this.minH;
      let maxW = this.maxW;
      let maxH = this.maxH;
      const [gridX, gridY] = this.grid;
      // 获取矩形信息
      const width = this.width;
      const height = this.height;
      const left = this.left;
      const top = this.top;
      const right = this.right;
      const bottom = this.bottom;
      // 对齐网格
      maxW = maxW - (maxW % gridX);
      maxH = maxH - (maxH % gridY);
      const limits = {
        minLeft: null,
        maxLeft: null,
        minTop: null,
        maxTop: null,
        minRight: null,
        maxRight: null,
        minBottom: null,
        maxBottom: null,
      };
      // 边界限制
      if (this.parent) {
        limits.minLeft = left;
        limits.maxLeft = left + Math.floor((width - minW) / gridX);
        limits.minTop = top;
        limits.maxTop = top + Math.floor((height - minH) / gridY);
        limits.minRight = right;
        limits.maxRight = right + Math.floor((width - minW) / gridX);
        limits.minBottom = bottom;
        limits.maxBottom = bottom + Math.floor((height - minH) / gridY);
        if (maxW) {
          limits.minLeft = Math.max(limits.minLeft, this.parentWidth - right - maxW);
          limits.minRight = Math.max(limits.minRight, this.parentWidth - left - maxW);
        }
        if (maxH) {
          limits.minTop = Math.max(limits.minTop, this.parentHeight - bottom - maxH);
          limits.minBottom = Math.max(limits.minBottom, this.parentHeight - top - maxH);
        }
      } else {
        limits.minLeft = null;
        limits.maxLeft = left + Math.floor(width - minW);
        limits.minTop = null;
        limits.maxTop = top + Math.floor(height - minH);
        limits.minRight = null;
        limits.maxRight = right + Math.floor(width - minW);
        limits.minBottom = null;
        limits.maxBottom = bottom + Math.floor(height - minH);
        if (maxW) {
          limits.minLeft = -(right + maxW);
          limits.minRight = -(left + maxW);
        }
        if (maxH) {
          limits.minTop = -(bottom + maxH);
          limits.minBottom = -(top + maxH);
        }
        if (this.lockAspectRatio && maxW && maxH) {
          limits.minLeft = Math.min(limits.minLeft, -(right + maxW));
          limits.minTop = Math.min(limits.minTop, -(maxH + bottom));
          limits.minRight = Math.min(limits.minRight, -left - maxW);
          limits.minBottom = Math.min(limits.minBottom, -top - maxH);
        }
      }
      return limits;
    },
    // 移动
    move(e) {
      if (this.resizing) {
        this.handleResize(e);
      } else if (this.dragging) {
        this.handleDrag(e);
      } else if (this.rotating) {
        this.handleRotate(e);
      }
    },
    // 获取鼠标或者触摸点的坐标
    getMouseCoordinate(e) {
      if (e.type.indexOf("touch") !== -1) {
        return {
          x: e.changedTouches[0].clientX,
          y: e.changedTouches[0].clientY,
        };
      } else {
        return {
          x: e.pageX || e.clientX + document.documentElement.scrollLeft,
          y: e.pageY || e.clientY + document.documentElement.scrollTop,
        };
      }
    },
    handleRotate(e) {
      // 获取方向向量，得到旋转角度
      const { x: mouseX, y: mouseY } = this.getMouseCoordinate(e);
      const x = mouseX - this.Center.x;
      const y = mouseY - this.Center.y;
      this.rotate = (getAngle(x, y) + 90) % 360;
      $emit(this, "rotating", this.rotate);
      // 元素移动
    },
    // 元素移动
    async handleDrag(e) {
      const axis = this.axis;
      const grid = this.grid;
      const bounds = this.bounds;
      const mouseClickPosition = this.mouseClickPosition;
      // 水平移动
      const tmpDeltaX =
        axis && axis !== "y" ? mouseClickPosition.mouseX - (e.touches ? e.touches[0].pageX : e.pageX) : 0;
      // 垂直移动
      const tmpDeltaY =
        axis && axis !== "x" ? mouseClickPosition.mouseY - (e.touches ? e.touches[0].pageY : e.pageY) : 0;
      const [deltaX, deltaY] = snapToGrid(grid, tmpDeltaX, tmpDeltaY, this.scaleRatio);
      const left = restrictToBounds(mouseClickPosition.left - deltaX, bounds.minLeft, bounds.maxLeft);
      const top = restrictToBounds(mouseClickPosition.top - deltaY, bounds.minTop, bounds.maxTop);
      if (this.onDrag(left, top) === false) {
        return;
      }
      const right = restrictToBounds(mouseClickPosition.right + deltaX, bounds.minRight, bounds.maxRight);
      const bottom = restrictToBounds(mouseClickPosition.bottom + deltaY, bounds.minBottom, bounds.maxBottom);
      this.left = left;
      this.top = top;
      this.right = right;
      this.bottom = bottom;
      await this.snapCheck();
      $emit(this, "dragging", this.left, this.top);
    },
    // 外部传参改动x
    moveHorizontally(val) {
      const [deltaX, ] = snapToGrid(this.grid, val, this.top, this.scale);
      const left = restrictToBounds(deltaX, this.bounds.minLeft, this.bounds.maxLeft);
      this.left = left;
      this.right = this.parentWidth - this.width - left;
    },
    // 外部传参改动y
    moveVertically(val) {
      const [, deltaY] = snapToGrid(this.grid, this.left, val, this.scale);
      const top = restrictToBounds(deltaY, this.bounds.minTop, this.bounds.maxTop);
      this.top = top;
      this.bottom = this.parentHeight - this.height - top;
    },
    // 控制柄移动
    handleResize(e) {
      const handle = this.handle;
      const scaleRatio = this.scaleRatio;
      const { TL, TR, BL, BR } = this;
      let { x: mouseX, y: mouseY } = this.getMouseCoordinate(e);
      // 在非旋转且有父容器限制的时候，直接限制mouse参与计算的坐标值
      if (!this.rotatable && this.parent) {
        mouseX = restrictToBounds(mouseX, this.parentX, this.parentX + this.parentWidth * scaleRatio);
        mouseY = restrictToBounds(mouseY, this.parentY, this.parentY + this.parentHeight * scaleRatio);
      }
      // 获取鼠标移动的坐标差
      let deltaX = mouseX - this.mouseClickPosition.mouseX;
      let deltaY = mouseY - this.mouseClickPosition.mouseY;
      // 考虑放缩
      deltaX = deltaX / scaleRatio;
      deltaY = deltaY / scaleRatio;
      let diffX, diffY, scale, scaleB, scaleC, newX, newY, newW, newH;
      let Fixed = {}; // 固定点
      let BX = {}; // 高度边选点
      let CX = {}; //  宽度边选点
      let Va = {}; // 固定点到鼠标 向量
      let Vb = {}; // 固定点到投影边  向量
      let Vc = {}; // 另一边投影
      let Vw = {}; // 宽度向量
      let Vh = {}; // 高度向量
      // 拖动中点
      if (handle.includes("m")) {
        switch (handle) {
          case "tm":
            diffX = deltaX + (TL.x + TR.x) / 2;
            diffY = deltaY + (TL.y + TR.y) / 2;
            Fixed = BL;
            BX = TL;
            CX = BR;
            Va = { x: diffX - Fixed.x, y: diffY - Fixed.y };
            Vb = { x: BX.x - Fixed.x, y: BX.y - Fixed.y };
            scale = (Va.x * Vb.x + Va.y * Vb.y) / (Math.pow(Vb.x, 2) + Math.pow(Vb.y, 2));
            Vw = { x: CX.x - Fixed.x, y: CX.y - Fixed.y };
            Vh = { x: Vb.x * scale, y: Vb.y * scale };
            break;
          case "bm":
            diffX = deltaX + (BL.x + BR.x) / 2;
            diffY = deltaY + (BL.y + BR.y) / 2;
            Fixed = TL;
            BX = BL;
            CX = TR;
            Va = { x: diffX - Fixed.x, y: diffY - Fixed.y };
            Vb = { x: BX.x - Fixed.x, y: BX.y - Fixed.y };
            scale = (Va.x * Vb.x + Va.y * Vb.y) / (Math.pow(Vb.x, 2) + Math.pow(Vb.y, 2));
            Vw = { x: CX.x - Fixed.x, y: CX.y - Fixed.y };
            Vh = { x: Vb.x * scale, y: Vb.y * scale };
            break;
          case "ml":
            diffX = deltaX + (TL.x + BL.x) / 2;
            diffY = deltaY + (TL.y + BL.y) / 2;
            Fixed = BR;
            BX = BL;
            CX = TR;
            Va = { x: diffX - Fixed.x, y: diffY - Fixed.y };
            Vb = { x: BX.x - Fixed.x, y: BX.y - Fixed.y };
            scale = (Va.x * Vb.x + Va.y * Vb.y) / (Math.pow(Vb.x, 2) + Math.pow(Vb.y, 2));
            Vh = { x: CX.x - Fixed.x, y: CX.y - Fixed.y };
            Vw = { x: Vb.x * scale, y: Vb.y * scale };
            break;
          case "mr":
            diffX = deltaX + (TR.x + TR.x) / 2;
            diffY = deltaY + (TR.y + TR.y) / 2;
            Fixed = BL;
            BX = BR;
            CX = TL;
            Va = { x: diffX - Fixed.x, y: diffY - Fixed.y };
            Vb = { x: BX.x - Fixed.x, y: BX.y - Fixed.y };
            scale = (Va.x * Vb.x + Va.y * Vb.y) / (Math.pow(Vb.x, 2) + Math.pow(Vb.y, 2));
            Vh = { x: CX.x - Fixed.x, y: CX.y - Fixed.y };
            Vw = { x: Vb.x * scale, y: Vb.y * scale };
            break;
          default:
            break;
        }
        // 反推宽高
        newX = Fixed.x + (Vw.x + Vh.x) / 2;
        newY = Fixed.y + (Vw.y + Vh.y) / 2;
        newW = Math.sqrt(Math.pow(Vw.x, 2) + Math.pow(Vw.y, 2));
        newH = Math.sqrt(Math.pow(Vh.x, 2) + Math.pow(Vh.y, 2));
      } else {
        // 拖动顶点
        switch (handle) {
          case "tl":
            diffX = deltaX + TL.x;
            diffY = deltaY + TL.y;
            Fixed = BR;
            BX = BL; // 高度 TL BL
            CX = TR; // 宽度 TL TR
            break;
          case "tr":
            diffX = deltaX + TR.x;
            diffY = deltaY + TR.y;
            Fixed = BL;
            BX = BR;
            CX = TL;
            break;
          case "bl":
            diffX = deltaX + BL.x;
            diffY = deltaY + BL.y;
            Fixed = TR;
            BX = TL;
            CX = BR;
            break;
          case "br":
            diffX = deltaX + BR.x;
            diffY = deltaY + BR.y;
            Fixed = TL;
            BX = TR;
            CX = BL;
            break;
          default:
            break;
        }
        Va = { x: diffX - Fixed.x, y: diffY - Fixed.y };
        Vb = { x: BX.x - Fixed.x, y: BX.y - Fixed.y };
        Vc = { x: CX.x - Fixed.x, y: CX.y - Fixed.y };
        scaleB = (Va.x * Vb.x + Va.y * Vb.y) / (Math.pow(Vb.x, 2) + Math.pow(Vb.y, 2));
        scaleC = (Va.x * Vc.x + Va.y * Vc.y) / (Math.pow(Vc.x, 2) + Math.pow(Vc.y, 2));
        Vw = { x: Vb.x * scaleB, y: Vb.y * scaleB };
        Vh = { x: Vc.x * scaleC, y: Vc.y * scaleC };
        // 反推宽高
        newX = Fixed.x + (Vw.x + Vh.x) / 2;
        newY = Fixed.y + (Vw.y + Vh.y) / 2;
        newW = Math.sqrt(Math.pow(Vw.x, 2) + Math.pow(Vw.y, 2));
        newH = Math.sqrt(Math.pow(Vh.x, 2) + Math.pow(Vh.y, 2));
      }
      this.left = newX - newW / 2;
      this.top = newY - newH / 2;
      // 大小限制
      newW = restrictToBounds(newW, this.minW || 0, this.maxW);
      newH = restrictToBounds(newH, this.minH || 0, this.maxH);
      // 父元素限制
      if (this.parent) {
        newW = restrictToBounds(newW, 0, this.parentWidth);
        newH = restrictToBounds(newH, 0, this.parentHeight);
      }
      // 纵横比限制
      if (this.lockAspectRatio) {
        if (newW / newH > this.aspectFactor) {
          newW = newH * this.aspectFactor;
        } else {
          newH = newW / this.aspectFactor;
        }
      }
      this.width = newW;
      this.height = newH;
      $emit(this, "resizing", this.left, this.top, this.width, this.height);
    },
    changeWidth(val) {
      const [newWidth, ] = snapToGrid(this.grid, val, 0, this.scale);
      // const right = restrictToBounds(
      //   this.parentWidth - newWidth - this.left,
      //   this.bounds.minRight,
      //   this.bounds.maxRight
      // );
      const right = this.parentWidth - newWidth - this.left;
      let bottom = this.bottom;
      if (this.lockAspectRatio) {
        bottom = this.bottom - (this.right - right) / this.aspectFactor;
      }
      const width = computeWidth(this.parentWidth, this.left, right);
      const height = computeHeight(this.parentHeight, this.top, bottom);
      this.right = right;
      this.bottom = bottom;
      this.width = width;
      this.height = height;
    },
    changeHeight(val) {
      const [, newHeight] = snapToGrid(this.grid, 0, val, this.scale);
      // const bottom = restrictToBounds(
      //   this.parentHeight - newHeight - this.top,
      //   this.bounds.minBottom,
      //   this.bounds.maxBottom
      // );
      const bottom = this.parentHeight - newHeight - this.top;
      let right = this.right;
      if (this.lockAspectRatio) {
        right = this.right - (this.bottom - bottom) * this.aspectFactor;
      }
      const width = computeWidth(this.parentWidth, this.left, right);
      const height = computeHeight(this.parentHeight, this.top, bottom);
      this.right = right;
      this.bottom = bottom;
      this.width = width;
      this.height = height;
    },
    // 从控制柄松开
    async handleUp(e) {
      this.handle = null;
      // 初始化辅助线数据
      const temArr = new Array(3).fill({
        display: false,
        position: "",
        origin: "",
        lineLength: "",
      });
      const refLine = { vLine: [], hLine: [] };
      for (const i in refLine) {
        refLine[i] = JSON.parse(JSON.stringify(temArr));
      }
      // 保存 鼠标松开的坐标
      const { x: mouseX, y: mouseY } = this.getMouseCoordinate(e);
      this.lastMouseX = mouseX;
      this.lastMouseY = mouseY;
      if (this.resizing) {
        this.resizing = false;
        await this.conflictCheck();
        $emit(this, "refLineParams", refLine);
        $emit(this, "resizestop", this.left, this.top, this.width, this.height);
      }
      if (this.dragging) {
        this.dragging = false;
        await this.conflictCheck();
        $emit(this, "refLineParams", refLine);
        $emit(this, "dragstop", this.left, this.top);
      }
      if (this.rotating) {
        this.rotating = false;
        $emit(this, "rotatestop", this.rotate);
      }
      this.resetBoundsAndMouseState();
      removeEvent(document.documentElement, eventsFor.move, this.move);
    },
    // 新增方法 ↓↓↓
    // 设置属性
    settingAttribute() {
      // 设置冲突检测
      this.$el.setAttribute("data-is-check", `${this.isConflictCheck}`);
      // 设置对齐元素
      this.$el.setAttribute("data-is-snap", `${this.snap}`);
    },
    // 冲突检测
    conflictCheck() {
      const top = this.top;
      const left = this.left;
      const width = this.width;
      const height = this.height;
      if (this.isConflictCheck) {
        const nodes = this.$el.parentNode.childNodes; // 获取当前父节点下所有子节点
        for (const item of nodes) {
          if (
            item.className !== undefined &&
            !item.className.split(" ").includes(this.classNameActive) &&
            item.getAttribute("data-is-check") !== null &&
            item.getAttribute("data-is-check") !== "false"
          ) {
            const tw = item.offsetWidth;
            const th = item.offsetHeight;
            // 正则获取left与right
            const [tl, tt] = this.formatTransformVal(item.style.transform);
            // 左上角与右下角重叠
            const tfAndBr =
              (top >= tt && left >= tl && tt + th > top && tl + tw > left) ||
              (top <= tt && left < tl && top + height > tt && left + width > tl);
            // 右上角与左下角重叠
            const brAndTf =
              (left <= tl && top >= tt && left + width > tl && top < tt + th) ||
              (top < tt && left > tl && top + height > tt && left < tl + tw);
            // 下边与上边重叠
            const bAndT =
              (top <= tt && left >= tl && top + height > tt && left < tl + tw) ||
              (top >= tt && left <= tl && top < tt + th && left > tl + tw);
            // 上边与下边重叠（宽度不一样）
            const tAndB =
              (top <= tt && left >= tl && top + height > tt && left < tl + tw) ||
              (top >= tt && left <= tl && top < tt + th && left > tl + tw);
            // 左边与右边重叠
            const lAndR =
              (left >= tl && top >= tt && left < tl + tw && top < tt + th) ||
              (top > tt && left <= tl && left + width > tl && top < tt + th);
            // 左边与右边重叠（高度不一样）
            const rAndL =
              (top <= tt && left >= tl && top + height > tt && left < tl + tw) ||
              (top >= tt && left <= tl && top < tt + th && left + width > tl);
            // 如果冲突，就将回退到移动前的位置
            if (tfAndBr || brAndTf || bAndT || tAndB || lAndR || rAndL) {
              this.top = this.mouseClickPosition.top;
              this.left = this.mouseClickPosition.left;
              this.width = this.mouseClickPosition.width;
              this.height = this.mouseClickPosition.height;
            }
          }
        }
      }
    },
    // 检测对齐元素
    async snapCheck() {
      if (this.snap) {
        // 保存当前元素的四个属性
        let width = this.width;
        let height = this.height;
        let activeLeft = this.left;
        let activeRight = this.left + width;
        let activeTop = this.top;
        let activeBottom = this.top + height;
        // 初始化辅助线数据
        const temArr = new Array(3).fill({
          display: false,
          position: "",
          origin: "",
          lineLength: "",
        });
        const refLine = { vLine: [], hLine: [] };
        for (const i in refLine) {
          refLine[i] = JSON.parse(JSON.stringify(temArr));
        }
        const tem = {
          value: { x: [[], [], []], y: [[], [], []] },
          display: [],
          position: [],
        };
        // 获取当前父节点下所有子节点
        const nodes = this.$el.parentNode.childNodes;
        //  当允许多个同时激活时，获取总体的属性
        const { groupWidth, groupHeight, groupLeft, groupTop, bln } = await this.getActiveAll(nodes);
        if (!bln) {
          width = groupWidth;
          height = groupHeight;
          activeLeft = groupLeft;
          activeRight = groupLeft + groupWidth;
          activeTop = groupTop;
          activeBottom = groupTop + groupHeight;
        }
        // 遍历获取其他元素的属性
        for (const item of nodes) {
          if (
            item.className !== undefined &&
            !item.className.split(" ").includes(this.classNameActive) &&
            item.getAttribute("data-is-snap") !== null &&
            item.getAttribute("data-is-snap") !== "false"
          ) {
            // 获取位置，角度
            const [l, t, rotate] = this.formatTransformVal(item.style.transform);
            if ((rotate - this.rotate) % 90 === 0) {
              // 获取宽高
              const w = item.offsetWidth;
              const h = item.offsetHeight;
              // 计算得到right和bottom
              const r = l + w; // 对齐目标right
              const b = t + h; // 对齐目标的bottom
              const hc = Math.abs(activeTop + height / 2 - (t + h / 2)) <= this.snapTolerance; // 水平中线
              const vc = Math.abs(activeLeft + width / 2 - (l + w / 2)) <= this.snapTolerance; // 垂直中线
              const ts = Math.abs(t - activeBottom) <= this.snapTolerance; // 从上到下
              const TS = Math.abs(b - activeBottom) <= this.snapTolerance; // 从上到下
              const bs = Math.abs(t - activeTop) <= this.snapTolerance; // 从下到上 上边共线
              const BS = Math.abs(b - activeTop) <= this.snapTolerance; // 从下到上
              const ls = Math.abs(l - activeRight) <= this.snapTolerance; // 外左
              const LS = Math.abs(r - activeRight) <= this.snapTolerance; // 外左
              const rs = Math.abs(l - activeLeft) <= this.snapTolerance; // 外右
              const RS = Math.abs(r - activeLeft) <= this.snapTolerance; // 外右
              tem.display = [ts, TS, bs, BS, hc, hc, ls, LS, rs, RS, vc, vc];
              tem.position = [t, b, t, b, t + h / 2, t + h / 2, l, r, l, r, l + w / 2, l + w / 2];
              // 单个可激活元素与其他元素对齐
              if (bln) {
                if (ts) {
                  this.top = t - height;
                  this.bottom = this.parentHeight - this.top - height;
                  tem.value.y[0].push(l, r, activeLeft, activeRight);
                }
                if (bs) {
                  this.top = t;
                  this.bottom = this.parentHeight - this.top - height;
                  tem.value.y[0].push(l, r, activeLeft, activeRight);
                }
                if (TS) {
                  this.top = b - height;
                  this.bottom = this.parentHeight - this.top - height;
                  tem.value.y[1].push(l, r, activeLeft, activeRight);
                }
                if (BS) {
                  this.top = b;
                  this.bottom = this.parentHeight - this.top - height;
                  tem.value.y[1].push(l, r, activeLeft, activeRight);
                }
                if (ls) {
                  this.left = l - width;
                  this.right = this.parentWidth - this.left - width;
                  tem.value.x[0].push(t, b, activeTop, activeBottom);
                }
                if (rs) {
                  this.left = l;
                  this.right = this.parentWidth - this.left - width;
                  tem.value.x[0].push(t, b, activeTop, activeBottom);
                }
                if (LS) {
                  this.left = r - width;
                  this.right = this.parentWidth - this.left - width;
                  tem.value.x[1].push(t, b, activeTop, activeBottom);
                }
                if (RS) {
                  this.left = r;
                  this.right = this.parentWidth - this.left - width;
                  tem.value.x[1].push(t, b, activeTop, activeBottom);
                }
                if (hc) {
                  this.top = t + h / 2 - height / 2;
                  this.bottom = this.parentHeight - this.top - height;
                  tem.value.y[2].push(l, r, activeLeft, activeRight);
                }
                if (vc) {
                  this.left = l + w / 2 - width / 2;
                  this.right = this.parentWidth - this.left - width;
                  tem.value.x[2].push(t, b, activeTop, activeBottom);
                }
                // 和容器贴边
                if (this.snapBorder) {
                  if (Math.abs(this.left - 0) <= this.snapTolerance) {
                    this.left = 0;
                    this.right = this.parentWidth - this.left - width;
                  }
                  if (Math.abs(this.right - 0) <= this.snapTolerance) {
                    this.right = 0;
                    this.left = this.parentWidth - this.width - this.right;
                  }
                  if (Math.abs(this.top - 0) <= this.snapTolerance) {
                    this.top = 0;
                    this.bottom = this.parentHeight - this.top - height;
                  }
                  if (Math.abs(this.bottom - 0) <= this.snapTolerance) {
                    this.bottom = 0;
                    this.top = this.parentHeight - this.bottom - height;
                  }
                }
              }
              // 再次进行边界处理
              let bounds = this.bounds;
              this.left = restrictToBounds(this.left, bounds.minLeft, bounds.maxLeft);
              this.top = restrictToBounds(this.top, bounds.minTop, bounds.maxTop);
              this.right = restrictToBounds(this.right, bounds.minRight, bounds.maxRight);
              this.bottom = restrictToBounds(this.bottom, bounds.minBottom, bounds.maxBottom);
              // 辅助线坐标与是否显示(display)对应的数组,易于循环遍历
              const arrTem = [0, 1, 0, 1, 2, 2, 0, 1, 0, 1, 2, 2];
              for (let i = 0; i <= arrTem.length; i++) {
                // 前6为Y辅助线,后6为X辅助线
                const xory = i < 6 ? "y" : "x";
                const horv = i < 6 ? "hLine" : "vLine";
                if (tem.display[i]) {
                  const { origin, length } = this.calcLineValues(tem.value[xory][arrTem[i]]);
                  refLine[horv][arrTem[i]].display = tem.display[i];
                  refLine[horv][arrTem[i]].position = tem.position[i] + "px";
                  refLine[horv][arrTem[i]].origin = origin;
                  refLine[horv][arrTem[i]].lineLength = length;
                }
              }
            }
          }
        }
        $emit(this, "refLineParams", refLine);
      }
    },
    // 计算参考线
    calcLineValues(arr) {
      const length = Math.max(...arr) - Math.min(...arr) + "px";
      const origin = Math.min(...arr) + "px";
      return { length, origin };
    },
    async getActiveAll(nodes) {
      const activeAll = [];
      const XArray = [];
      const YArray = [];
      let groupWidth = 0;
      let groupHeight = 0;
      let groupLeft = 0;
      let groupTop = 0;
      for (const item of nodes) {
        // 修复判断条件 split(' ')
        if (item.className !== undefined && item.className.split(" ").includes(this.classNameActive)) {
          activeAll.push(item);
        }
      }
      const AllLength = activeAll.length;
      if (AllLength > 1) {
        for (const i of activeAll) {
          const l = i.offsetLeft;
          const r = l + i.offsetWidth;
          const t = i.offsetTop;
          const b = t + i.offsetHeight;
          XArray.push(l, r);
          YArray.push(t, b);
        }
        groupWidth = Math.max(...XArray) - Math.min(...XArray);
        groupHeight = Math.max(...YArray) - Math.min(...YArray);
        groupLeft = Math.min(...XArray);
        groupTop = Math.min(...YArray);
      }
      const bln = AllLength === 1;
      return { groupWidth, groupHeight, groupLeft, groupTop, bln };
    },
    // 修复 正则获取left与top  string.match(/[\d|\.]+/g)
    formatTransformVal(string) {
      let [left, top, rotate = 0] = string.match(/[\d|.]+/g);
      if (top === undefined) top = 0;
      return [Number(left), Number(top), rotate];
    },
  },
  emits: [
    "update:active",
    "rotating",
    "dragging",
    "resizing",
    "refLineParams",
    "resizestop",
    "dragstop",
    "rotatestop",
    "activated",
    "deactivated",
  ],
};
</script>

<style>
.vue-drag-resize-rotate {
  touch-action: none;
  position: absolute;
  box-sizing: border-box;
  border: 1px dashed;
}

.vue-drag-resize-rotate .handle {
  box-sizing: border-box;
  position: absolute;
  background: #ffffff;
  border: 1px solid #333;
}

.vue-drag-resize-rotate .handle-tl {
  cursor: nw-resize;
}

.vue-drag-resize-rotate .handle-tm {
  cursor: n-resize;
}

.vue-drag-resize-rotate .handle-tr {
  cursor: ne-resize;
}

.vue-drag-resize-rotate .handle-ml {
  cursor: w-resize;
}

.vue-drag-resize-rotate .handle-mr {
  cursor: e-resize;
}

.vue-drag-resize-rotate .handle-bl {
  cursor: sw-resize;
}

.vue-drag-resize-rotate .handle-bm {
  cursor: s-resize;
}

.vue-drag-resize-rotate .handle-br {
  cursor: se-resize;
}

.vue-drag-resize-rotate .handle-rot {
  transform: translateX(-50%);
  cursor: grab;
  display: inline-block;
  box-sizing: border-box;
  border: none;
  text-indent: -9999px;
  vertical-align: middle;
}

.vue-drag-resize-rotate .handle-rot:before,
.vue-drag-resize-rotate .handle-rot:after {
  content: "";
  box-sizing: inherit;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.vue-drag-resize-rotate .handle-rot:before {
  width: 1em;
  height: 1em;
  border: 2px solid #333;
  border-right-color: transparent;
  border-radius: 50%;
}

.handle-rot:after {
  width: 0px;
  height: 0px;
  border: 0.25em solid #333;
  border-left-color: transparent;
  border-top-color: transparent;
  left: 100%;
  top: 10%;
}
</style>
